/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.portlet.editor.model.portlet.factory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.genia.toolbox.basics.editor.model.document.VariableModel;
import com.genia.toolbox.portlet.editor.PortletEditorApplication;
import com.genia.toolbox.portlet.editor.model.portlet.PortletType;
import com.genia.toolbox.portlet.editor.model.portlet.impl.ContainerPortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.DispatcherPortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.HoleModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.HolePortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.LinkedPortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.PortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.SimplePortletModel;
import com.genia.toolbox.portlet.editor.model.portlet.impl.TemplatePortletModel;
import com.genia.toolbox.web.portlet.bean.Portlet;
import com.genia.toolbox.web.portlet.bean.impl.ContainerPortlet;
import com.genia.toolbox.web.portlet.bean.impl.DispatcherPortlet;
import com.genia.toolbox.web.portlet.bean.impl.HolePortlet;
import com.genia.toolbox.web.portlet.bean.impl.LinkedPortlet;
import com.genia.toolbox.web.portlet.bean.impl.SimplePortlet;
import com.genia.toolbox.web.portlet.bean.impl.TemplatePortlet;
import com.genia.toolbox.web.portlet.description.ContainerPortletDescription;
import com.genia.toolbox.web.portlet.description.PortletDescription;
import com.genia.toolbox.web.portlet.description.SimplePortletDescription;
import com.genia.toolbox.web.portlet.description.VariableDescription;
import com.genia.toolbox.web.portlet.description.impl.LongVariableDescription;

/**
 * The model factory.
 */
public class PortletModelFactory
{

  /**
   * The portletEditorApplication.
   */
  private PortletEditorApplication portletEditorApplication;



  /**
   * Constructor.
   * 
   * @param portletEditorApplication
   *          the portletEditorApplication.
   */
  public PortletModelFactory(PortletEditorApplication portletEditorApplication)
  {
    super();
    this.portletEditorApplication = portletEditorApplication;
  }



  /**
   * Convert a portlet to its model.
   * 
   * @param portlet
   *          the portlet.
   * @return the portlet model.
   */
  public PortletModel convert(Portlet portlet)
  {
    PortletModel portletModel = null;
    PortletType portletType = this.getPortletType(portlet);
    PortletDescription portletDescription = null;
    if (PortletType.Container.equals(portletType)) {
      ContainerPortletDescription containerDescription = (ContainerPortletDescription) this.portletEditorApplication.getContainerPortletDescription(portlet.getDescriptionName());
      ContainerPortlet containerPortlet = (ContainerPortlet) portlet;
      ContainerPortletModel containerPortletModel = new ContainerPortletModel();
      this.copyCommonSettings(containerPortletModel, portlet, portletType, containerDescription);
      containerPortletModel.setAttributes(this.retrieveAttributes(containerPortlet, containerDescription));
      containerPortletModel.setMaxNbSubPortlets(containerDescription.getMaxNumberSubPortlets());
      containerPortletModel.setMinNbSubPortlets(containerDescription.getMinNumberSubPortlets());
      containerPortletModel.setDirection(containerDescription.getLayoutDirection());
      containerPortletModel.setView(containerDescription.getViewName());
      List<PortletModel> subportlets = new ArrayList<PortletModel>();
      PortletModel subportletModel = null;
      for (Portlet subportlet : containerPortlet.getSubPortlets()) {
        subportletModel = this.convert(subportlet);
        subportletModel.setSubElement(true);
        subportletModel.setParentID(containerPortletModel.getUniqueID());
        subportletModel.setDocumentID(containerPortletModel.getDocumentID());
        subportletModel.setParentDirection(containerPortletModel.getDirection());
        subportlets.add(subportletModel);
      }
      containerPortletModel.setSubportlets(subportlets);
      portletModel = containerPortletModel;
    }
    else if (PortletType.Linked.equals(portletType)) {
      LinkedPortlet linkedPortlet = (LinkedPortlet) portlet;
      LinkedPortletModel linkedPortletModel = new LinkedPortletModel();
      linkedPortletModel.setLink(linkedPortlet.getLinkedPortletName());
      this.copyCommonSettings(linkedPortletModel, linkedPortlet, portletType, portletDescription);
      portletModel = linkedPortletModel;
    }
    else if (PortletType.Simple.equals(portletType)) {
      SimplePortletDescription simpleDescription = (SimplePortletDescription) this.portletEditorApplication.getSimplePortletDescription(portlet.getDescriptionName());
      SimplePortlet simplePortlet = (SimplePortlet) portlet;
      SimplePortletModel simplePortletModel = new SimplePortletModel();
      this.copyCommonSettings(simplePortletModel, simplePortlet, portletType, simpleDescription);
      simplePortletModel.setController(simplePortlet.getControllerName());
      simplePortletModel.setAttributes(this.retrieveAttributes(simplePortlet, simpleDescription));
      simplePortletModel.setParameters(this.retrieveParameters(simplePortlet, simpleDescription));
      portletModel = simplePortletModel;
    }
    else if (PortletType.Dispatcher.equals(portletType)) {
      DispatcherPortlet dispatcherPortlet = (DispatcherPortlet) portlet;
      DispatcherPortletModel dispatcherPortletModel = new DispatcherPortletModel();
      dispatcherPortletModel.setAttributes(this.retrieveAttributes(dispatcherPortlet, null));
      this.copyCommonSettings(dispatcherPortletModel, portlet, portletType, portletDescription);
      dispatcherPortletModel.setUrl(dispatcherPortlet.getUrl());
      portletModel = dispatcherPortletModel;
    }
    else if (PortletType.Hole.equals(portletType)) {
      HolePortlet holePortlet = (HolePortlet) portlet;
      HolePortletModel holePortletModel = new HolePortletModel();
      holePortletModel.setHoleName(holePortlet.getHoleName());
      this.copyCommonSettings(holePortletModel, holePortlet, portletType, portletDescription);
      portletModel = holePortletModel;
    }
    else if (PortletType.Template.equals(portletType)) {
      TemplatePortlet templatePortlet = (TemplatePortlet) portlet;
      TemplatePortletModel templatePortletModel = new TemplatePortletModel();
      templatePortletModel.setLink(templatePortlet.getLinkedPortletName());
      this.copyCommonSettings(templatePortletModel, templatePortlet, portletType, portletDescription);
      List<HoleModel> holesModel = new ArrayList<HoleModel>();
      if (templatePortlet.getHolesPortlets() != null) {
        HoleModel holeModel = null;
        for (String key : templatePortlet.getHolesPortlets().keySet()) {
          holeModel = new HoleModel(key, templatePortlet.getHolesPortlets().get(key));
          holesModel.add(holeModel);
        }
      }
      templatePortletModel.setHoles(holesModel);
      portletModel = templatePortletModel;
    }
    return portletModel;
  }



  /**
   * Copy the common settings from a portlet to its model.
   * 
   * @param portletModel
   *          the portlet model.
   * @param portlet
   *          the portlet.
   * @param portletType
   *          the portlet type.
   * @param description
   *          the portlet description.
   */
  private void copyCommonSettings(PortletModel portletModel, Portlet portlet, PortletType portletType, PortletDescription description)
  {
    if (!PortletType.Linked.equals(portletType) && !PortletType.Hole.equals(portletType) && !PortletType.Template.equals(portletType)) {
      portletModel.setId(portlet.getIdentifier());
    }
    else {
      portletModel.setId("");
    }
    if (description != null) {
      portletModel.setDescriptionName(description.getName());
      portletModel.setElementName(description.getName());
      portletModel.setDescription(description.getDescription());
    }
    else {
      portletModel.setElementName("");
      portletModel.setDescription(null);
    }
    portletModel.setType(portletType);
  }



  /**
   * Retrieve the attributes from a description to a portlet.
   * 
   * @param portlet
   *          the portlet.
   * @param description
   *          the description.
   * @return the attributes.
   */
  private List<VariableModel> retrieveAttributes(Portlet portlet, PortletDescription description)
  {
    List<VariableModel> variables = new ArrayList<VariableModel>();
    if (portlet.getAttributes() != null && !portlet.getAttributes().isEmpty()) {
      Map<String, String> attributes = portlet.getAttributes();
      List<VariableDescription> descriptions = null;
      if (description != null) {
        descriptions = description.getAttributeDescription();
      }
      for (String name : attributes.keySet()) {
        if (descriptions != null) {
          VariableDescription variable = this.getVariable(descriptions, name);
          if (variable != null) {
            boolean isLongValue = false;
            if (variable instanceof LongVariableDescription) {
              isLongValue = true;
            }
            variables.add(new VariableModel(variable.getName(), variable.getDescription(), attributes.get(name), variable.getValues(), variable.isMandatory(), isLongValue));
          }
          else {
            variables.add(new VariableModel(name, attributes.get(name)));
          }
        }
        else {
          variables.add(new VariableModel(name, attributes.get(name)));
        }
      }
    }
    else if (description != null) {
      List<VariableDescription> descriptions = description.getAttributeDescription();
      Map<String, String> attributes = portlet.getAttributes();
      for (VariableDescription variable : descriptions) {
        boolean isLongValue = false;
        if (variable instanceof LongVariableDescription) {
          isLongValue = true;
        }
        variables.add(new VariableModel(variable.getName(), variable.getDescription(), attributes.get(variable.getName()), variable.getValues(), variable.isMandatory(), isLongValue));
      }
    }
    return variables;
  }



  /**
   * Retrieve the parameters from a description to a portlet.
   * 
   * @param portlet
   *          the portlet.
   * @param description
   *          the description.
   * @return the parameters.
   */
  private List<VariableModel> retrieveParameters(Portlet portlet, PortletDescription description)
  {
    List<VariableModel> variables = new ArrayList<VariableModel>();
    SimplePortlet simplePortlet = (SimplePortlet) portlet;
    SimplePortletDescription simpleDescription = (SimplePortletDescription) description;
    if (simplePortlet.getParameters() != null && !simplePortlet.getParameters().isEmpty()) {
      Map<String, String> parameters = simplePortlet.getParameters();
      List<VariableDescription> descriptions = null;
      if (simpleDescription != null) {
        descriptions = simpleDescription.getParameterDescription();
      }
      for (String name : parameters.keySet()) {
        if (descriptions != null) {
          VariableDescription variable = this.getVariable(descriptions, name);
          if (variable != null) {
            boolean isLongValue = false;
            if (variable instanceof LongVariableDescription) {
              isLongValue = true;
            }
            variables.add(new VariableModel(variable.getName(), variable.getDescription(), parameters.get(name), variable.getValues(), variable.isMandatory(), isLongValue));
          }
          else {
            variables.add(new VariableModel(name, parameters.get(name)));
          }
        }
        else {
          variables.add(new VariableModel(name, parameters.get(name)));
        }
      }
    }
    else if (description != null) {
      List<VariableDescription> descriptions = simpleDescription.getParameterDescription();
      Map<String, String> parameters = simplePortlet.getParameters();
      for (VariableDescription variable : descriptions) {
        boolean isLongValue = false;
        if (variable instanceof LongVariableDescription) {
          isLongValue = true;
        }
        variables.add(new VariableModel(variable.getName(), variable.getDescription(), parameters.get(variable.getName()), variable.getValues(), variable.isMandatory(), isLongValue));
      }
    }
    return variables;
  }



  /**
   * Get a variable from the list.
   * 
   * @param descriptions
   *          the variable descriptions.
   * @param name
   *          the name.
   * @return the variable.
   */
  private VariableDescription getVariable(List<VariableDescription> descriptions, String name)
  {
    VariableDescription variableDescription = null;
    for (VariableDescription variable : descriptions) {
      if (variable.getName().compareTo(name) == 0) {
        variableDescription = variable;
      }
    }
    return variableDescription;
  }



  /**
   * Convert a portlet model to a portlet.
   * 
   * @param portletModel
   *          the portlet model.
   * @return the portlet.
   */
  public Portlet convert(PortletModel portletModel)
  {
    Portlet portlet = null;
    PortletType portletType = portletModel.getType();
    if (PortletType.Container.equals(portletType)) {
      ContainerPortletModel containerModel = (ContainerPortletModel) portletModel;
      ContainerPortlet containerPortlet = new ContainerPortlet();
      containerPortlet.setAttributes(this.convertToMap(containerModel.getAttributes()));
      containerPortlet.setIdentifier(containerModel.getId());
      containerPortlet.setDescriptionName(containerModel.getDescriptionName());
      List<Portlet> subportlets = containerPortlet.getSubPortlets();
      for (PortletModel subportletModel : containerModel.getSubportlets()) {
        subportlets.add(this.convert(subportletModel));
      }
      containerPortlet.setSubPortlets(subportlets);
      containerPortlet.setViewName(containerModel.getView());
      portlet = containerPortlet;
    }
    else if (PortletType.Linked.equals(portletType)) {
      LinkedPortletModel linkedModel = (LinkedPortletModel) portletModel;
      LinkedPortlet linkedPortlet = new LinkedPortlet();
      linkedPortlet.setLinkedPortletName(linkedModel.getLink());
      portlet = linkedPortlet;
    }
    else if (PortletType.Dispatcher.equals(portletType)) {
      DispatcherPortletModel dispatcherModel = (DispatcherPortletModel) portletModel;
      DispatcherPortlet dispatcherPortlet = new DispatcherPortlet();
      dispatcherPortlet.setAttributes(this.convertToMap(dispatcherModel.getAttributes()));
      dispatcherPortlet.setUrl(dispatcherModel.getUrl());
      dispatcherPortlet.setIdentifier(dispatcherModel.getId());
      dispatcherPortlet.setDescriptionName(dispatcherModel.getDescriptionName());
      portlet = dispatcherPortlet;
    }
    else if (PortletType.Simple.equals(portletType)) {
      SimplePortletModel simpleModel = (SimplePortletModel) portletModel;
      SimplePortlet simplePortlet = new SimplePortlet();
      simplePortlet.setAttributes(this.convertToMap(simpleModel.getAttributes()));
      simplePortlet.setIdentifier(simpleModel.getId());
      simplePortlet.setControllerName(simpleModel.getController());
      simplePortlet.setParameters(this.convertToMap(simpleModel.getParameters()));
      simplePortlet.setDescriptionName(simpleModel.getDescriptionName());
      portlet = simplePortlet;
    }
    else if (PortletType.Hole.equals(portletType)) {
      HolePortletModel holeModel = (HolePortletModel) portletModel;
      HolePortlet holePortlet = new HolePortlet();
      holePortlet.setHoleName(holeModel.getHoleName());
      portlet = holePortlet;
    }
    else if (PortletType.Template.equals(portletType)) {
      TemplatePortletModel templateModel = (TemplatePortletModel) portletModel;
      TemplatePortlet templatePortlet = new TemplatePortlet();
      Map<String, String> holes = new HashMap<String, String>();
      for (HoleModel holeModel : templateModel.getHoles()) {
        holes.put(holeModel.getName(), holeModel.getValue());
      }
      templatePortlet.setHolesPortlets(holes);
      templatePortlet.setLinkedPortletName(templateModel.getLink());
      portlet = templatePortlet;
    }
    return portlet;
  }



  /**
   * Convert the variables to a map.
   * 
   * @param variables
   *          the variables.
   * @return the map.
   */
  private Map<String, String> convertToMap(List<VariableModel> variables)
  {
    Map<String, String> map = new HashMap<String, String>();
    for (VariableModel variable : variables) {
      map.put(variable.getName(), variable.getValue());
    }
    return map;
  }



  /**
   * Get the portlet type of the portlet.
   * 
   * @param portlet
   *          the portlet.
   * @return the portlet type.
   */
  private PortletType getPortletType(Portlet portlet)
  {
    PortletType type = null;
    if (portlet instanceof ContainerPortlet) {
      type = PortletType.Container;
    }
    else if (portlet instanceof DispatcherPortlet) {
      type = PortletType.Dispatcher;
    }
    else if (portlet instanceof SimplePortlet) {
      type = PortletType.Simple;
    }
    else if (portlet instanceof HolePortlet) {
      type = PortletType.Hole;
    }
    else if (portlet instanceof TemplatePortlet) {
      type = PortletType.Template;
    }
    else if (portlet instanceof LinkedPortlet) {
      type = PortletType.Linked;
    }
    return type;
  }

}
